﻿/* i2c_hal.c */

#include "i2c_hal.h"

void i2c_slave_xfer_done_hook(void);

/* 配置master轮询方式. */
void i2c_conf_master(I2C_Type * base, i2c_speed_t speed)
{
    CLOCK_EnableClock(kCLOCK_I2c0);

    /* set bit rate. */
    base->CLKDIV = I2C_CLKDIV_DIVVAL(0x1); /* 原始时钟源的2分频作为i2c_clk. */
    base->MSTTIME = I2C_MSTTIME_MSTSCLLOW(0x7) | I2C_MSTTIME_MSTSCLHIGH(0x7); /* SCL基于i2c_clk的波形. */

    /* master function. */
    base->CFG = I2C_CFG_MSTEN_MASK; /* enable master. */
}

/* bus上的读, 不是协议的读. */
void i2c_master_read(I2C_Type * base, uint8_t dev_addr, uint8_t *buff, uint8_t len)
{
     /* start with target address, read. */
    base->MSTDAT = I2C_DEV_ADDR(dev_addr) | I2C_DEV_ADDR_READ_MASK; /* read. */
    base->MSTCTL = I2C_MSTCTL_MSTSTART_MASK;
    /* wait while the i2c master is sending the slave address. */
    while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
    {}

    /* read data from slave. */
    while ( len > 1)
    {
        /* wait while the i2c master is sending the slave address. */
        while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
        {}
        *buff++ = (uint8_t)(base->MSTDAT);
        base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK; /* send ack to slave for next byte. */
        len--;
    }

    /* read the final byte ending with nack and stop */
    while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
    {}
    *buff = (uint8_t)(base->MSTDAT);
    base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK; /* send stop with nack. */
}

void i2c_master_write(I2C_Type * base, uint8_t dev_addr, uint8_t *buff, uint8_t len)
{
     /* start with target address. */
    base->MSTDAT = I2C_DEV_ADDR(dev_addr) | I2C_DEV_ADDR_WRITE_MASK; /* write. */
    base->MSTCTL = I2C_MSTCTL_MSTSTART_MASK;
    /* wait while the i2c master is sending the slave address. */
    while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
    {}

    /* send data context. */
    for (uint8_t i = 0u; i < len; i++)
    {
        base->MSTDAT = buff[i];
        base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK;
        /* wait while the i2c master is sending data. */
        while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
        {}
    }

    /* send stop. */
    base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK;
}

#if 1

/*******************************************************************************
 * i2c slave.
 *******************************************************************************/
volatile uint8_t *i2c_slave_data_buf = NULL;
volatile uint8_t i2c_slave_data_len = 0u;
volatile uint8_t i2c_slave_data_idx = 0u;

volatile i2c_func1_t i2c0_slave_xfer_done_callback_func;

void i2c_conf_slave(I2C_Type * base, uint8_t slave_addr, uint8_t *data_buf, uint8_t data_len)
{
    CLOCK_EnableClock(kCLOCK_I2c0);

    i2c_slave_data_buf = data_buf;
    i2c_slave_data_len = data_len;
    i2c_slave_data_idx = 0u;

    base->CFG = I2C_CFG_SLVEN_MASK; /* enable slave mode. */

    base->SLVADR[0] = I2C_DEV_ADDR(slave_addr); /* set slave addr and enable it. */

    i2c0_slave_xfer_done_callback_func = NULL;

    /* enable interrupt. */
    //base->INTSTAT = I2C_INTSTAT_SLVPENDING_MASK | I2C_INTSTAT_SLVDESEL_MASK; /* clear flags. */
    base->INTENSET = I2C_INTENSET_SLVPENDINGEN_MASK | I2C_INTENSET_SLVDESELEN_MASK;
    NVIC_EnableIRQ(I2C0_IRQn);
}

void i2c_disable_slave(I2C_Type *base)
{
    base->CFG &= ~I2C_CFG_SLVEN_MASK;
}

void i2c_enable_slave(I2C_Type *base)
{
    base->CFG |= I2C_CFG_SLVEN_MASK;
}


void i2c_slave_install_stop_callback(I2C_Type *base, i2c_func1_t func)
{
    if (base == I2C0)
    {
        i2c0_slave_xfer_done_callback_func = func;
    }
}

/* 在应用层由i2c中断程序调用. */
//volatile bool i2c_slave_flag_first_receive_after_address = false;
volatile uint8_t i2c_slave_addr; /* the dummy read for address period. */
void i2c_slave_isr_hook(I2C_Type *base)
{
    uint32_t flags = base->STAT;

    if ( 0u != (flags & I2C_STAT_SLVDESEL_MASK) ) /* stop comes. */
    {
        base->STAT = I2C_STAT_SLVDESEL_MASK; /* clear flag. */
        //i2c_slave_xfer_done_hook();

        if ( (base == I2C0) &&  (i2c0_slave_xfer_done_callback_func) )
        {
            i2c0_slave_xfer_done_callback_func(flags);
        }
    }
    else if (0u != (flags & I2C_STAT_SLVPENDING_MASK) )
    {
        uint32_t _i2c_slave_state = (flags & I2C_STAT_SLVSTATE_MASK)>>I2C_STAT_SLVSTATE_SHIFT;
        bool _i2c_slave_err = false;

        switch (_i2c_slave_state)
        {
        case 0u: /* Slave address. Address plus R/W received. At least  one of the four slave addresses has been matched by hardware. */
            /* check which slave address (1 in 4) is matched. read address from slvdata reg, or check the flags. */
            i2c_slave_addr= (uint8_t)(base->SLVDAT);
            //i2c_slave_flag_first_receive_after_address = true;
            i2c_slave_data_idx = 0u; /* always start from 0. */
            break;
        case 1u: /*  Slave receive. Received data is available (Slave Receiver mode).*/
            if (i2c_slave_data_idx <i2c_slave_data_len)
            {
                 i2c_slave_data_buf[i2c_slave_data_idx++] = (uint8_t)(base->SLVDAT);
            }
            else
            {
                volatile uint8_t _dummy = (uint8_t)(base->SLVDAT);
                _i2c_slave_err = true;
            }
            break;
        case 2u: /* Slave transmit. Data can be transmitted (Slave Transmitter mode). */
            if (i2c_slave_data_idx <i2c_slave_data_len)
            {
                 base->SLVDAT = i2c_slave_data_buf[i2c_slave_data_idx++];
            }
            else
            {
                base->SLVDAT = 0x0;
                _i2c_slave_err = true;
            }
            break;
        default:
            break;
        }

        if (_i2c_slave_err)
        {
            base->SLVCTL = I2C_SLVCTL_SLVNACK_MASK;
        }
        else
        {
            base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK;
        }
    }
}

void I2C0_IRQHandler(void)
{
    i2c_slave_isr_hook(I2C0);
}


#endif

/* EOF. */

